home *** CD-ROM | disk | FTP | other *** search
- /*
- * File: mf_intp.c
- * SGoldthorpe 9-Apr-91
- */
-
- /*
- * mf_intp - play standard midi files (sometimes)
- * FREEWARE (C) 1991 Stephen Goldthorpe - Take all you want! But please it be
- * known if you've made mods. I don't want a lot of complaints about code I've
- * never even seen before (life's hard enough without all of that)!
- * SGoldthorpe.wgc-e@rx.xerox.com
- * goldthor@arisia.xerox.com
- */
-
- /*
- * REVISION LOG
- * ============
- * 0.1 SGoldthorpe 20-Mar-91 Created for Atari ST / Sozobon C. It's
- * a bit atari specific in places buy i've
- * tried to make it UNIX(tm) looking for
- * easier porting (if anyone feels brave
- * enough to try.
- * 0.2 SGoldthorpe 7-Apr-91 Messed up the code in mf_intp to
- * allow type 1 midi files. Timing is
- * still a bit hairy but it plays 80%
- * of the files I have OK.
- */
-
- #include <stdio.h>
- #include <types.h>
- #include <time.h>
- #include <string.h>
-
- /* and for the atari OS stuff */
- #include <osbind.h>
-
- #ifndef FALSE
- #define FALSE 0
- #define TRUE (!FALSE)
- #endif
-
- /* MACRO DEFS */
- #define MAX_TRACKS 32
-
- /* EXTERNAL DECLS */
- extern char *app_name;
-
- /* GLOBAL VARIABLES */
- static double track_delta[MAX_TRACKS], division, time_per_beat;
- static WORD format, tracks;
- static int finished_tracks;
- static clock_t clock_orig;
- static char *gFile;
-
- /* FUNCTION DECLS */
- int mf_intp();
- static void truncated(), parse_error();
-
- /* MACRO FUNCTIONS */
- /* the error checking may be a bit OTT but I'm gonna do it anyway (helps
- catch those naughty bugs - and bad files) */
- #define GET32BITS(dw,p,l) dw=(((LONG)(*p)<<24)+ \
- ((LONG)(*(p+1))<<16)+ \
- (((LONG)*(p+2))<<8)+ \
- (LONG)(*(p+3))); \
- if(l<4) \
- { truncated(); \
- return(-1); \
- }; \
- p += 4; l -= 4
-
- #define GET16BITS(w,p,l) w=(((WORD)(*p)<<8)+(WORD)*(p+1)); \
- if(l<2) \
- { truncated(); \
- return(-1); \
- } \
- p += 2; l -= 2
-
- #define GET8BITS(b,p,l) b = *(p)++; \
- if(--l<0) \
- { truncated(); \
- return(-1); \
- }
-
- #define GETVARLEN(dw,p,l) for(dw=(LONG)(*p)&0x7f;(*(p)++)&0x80;) \
- { if(--l<0) \
- { truncated(); \
- return(-1); \
- }; \
- dw <<=7; \
- dw |= (LONG)(*p)&0x7f; \
- }; \
- if(--l<0) \
- { truncated(); \
- return(-1); \
- }
-
- #define CHECKLEFT(l,v) if(l<v) \
- { truncated(); \
- return(-1); \
- }
-
- #define SEND(b) Bconout(3,b)
-
- /* FUNCTION DEFS */
- int mf_intp(buffer, file, len)
- char *file;
- BYTE *buffer;
- unsigned int len;
- { BYTE *track_pos[MAX_TRACKS];
- int track_left[MAX_TRACKS];
- int i;
- int track_finished[MAX_TRACKS];
- BYTE *pos=buffer,*next;
- BYTE running_status;
- WORD w;
- LONG dw,delta;
- double clock_delta;
- unsigned int left=len;
-
- gFile=file;
-
- /* check header */
- if((left<4)||(strncmp("MThd",(char*)pos,4)!=0))
- { (void)fprintf(stderr,
- "%s: %s is not a midi file\n",app_name,file);
- return(-1);
- };
- pos += 4; left -= 4;
-
- /* find address of next chunk */
- GET32BITS(dw,pos,left);
- next=pos+dw;
- #ifdef DEBUG
- (void)printf(
- "pos is %lx len is %ld next would be %lx\n",pos-buffer,dw,
- next-buffer);
- #endif
-
- /* get file format */
- GET16BITS(format,pos,left);
- switch (format)
- { case 0:
- case 1:
- /* OK we accept formats 0 and 1 */
- break;
- default:
- /* but we don't do any others */
- (void)fprintf(stderr,
- "%s: can't play %s, midi file type %d\n",
- app_name, file,format);
- return(-1);
- };
-
- /* get number of tracks */
- GET16BITS(tracks,pos,left);
-
- /* check tracks in range */
- if(tracks > MAX_TRACKS)
- { (void)fprintf(stderr,
- "%s: %s has too many tracks (%d allowed).\n",app_name,
- file, tracks, MAX_TRACKS);
- return(-1);
- };
-
- /* get division - this is the division of a quarter note or
- if negative is frame based. */
- GET16BITS(w,pos,left);
- division=(double)w;
-
- /* I don't suport the frame stuff yet! */
- if(division < 0)
- { (void)fprintf(stderr,
- "%s: file %s - don't support framed based files yet!",
- app_name, file);
- return(-1);
- };
- #ifdef DEBUG
- (void)printf("division = %f\n",division);
- #endif
-
- time_per_beat = (double)(CLK_TCK * 60.0)/120.0; /* default 120bpm */
- #ifdef DEBUG
- (void)printf("time per beat (1/200 sec) %f\n",time_per_beat);
- #endif
-
- /* do some initialisation, track finding etc */
- running_status = 0xfe;
- finished_tracks = 0;
- for(i=0;i<tracks;i++)
- { CHECKLEFT(left,(next-pos));
- left -= next-pos; pos = next;
-
- /* track start */
- if((left<4)||(strncmp("MTrk",(char*)pos,4)!=0))
- { (void)fprintf(stderr,
- "%s: %s parse error, track expected\n",app_name,
- file);
- #ifdef DEBUG
- (void)printf(
- "posn %lx, bytes around (-3..3) %02x %02x %02x \
- %02x %02x %02x %02x\n",pos-buffer,*(pos-3),*(pos-2),*(pos-1),*pos,*(pos+1),
- *(pos+2),*(pos+3));
- #endif
- return(-1);
- };
- pos += 4; left -= 4;
-
- /* find address of next chunk */
- GET32BITS(dw,pos,left);
- *(track_pos+i) = pos;
- next = pos+dw;
- *(track_left+i) = (int)dw;
- #ifdef DEBUG
- (void)printf("pos is %lx len is %ld next would be %lx\n",
- pos-buffer,dw,next-buffer);
- #endif
-
- /* get initial delta time */
- GETVARLEN(dw,*(track_pos+i),*(track_left+i));
- *(track_delta+i)=(double)dw;
- *(track_finished+i)=FALSE;
- }
-
- /* let the user know what's happening */
- (void)printf("playing '%s' with %d tracks\n",file,tracks);
-
- /* get start time */
- clock_orig=clock();
-
- /* dispatcher - needs some work with timing etc doubles aren't really
- accurate enough (size wise) */
- while (finished_tracks != tracks)
- { BYTE event;
- clock_delta = (double)(clock()-clock_orig)*division/
- time_per_beat;
- for(i=0;i<tracks;i++)
- { if(!*(track_finished+i) & (clock_delta>
- *(track_delta+i)))
- { GET8BITS(event,*(track_pos+i),*(track_left+i));
-
- /* parse event */
- switch (event)
- { /* meta-events */
- case 0xff:
- { BYTE type;
- LONG length;
- GET8BITS(type,*(track_pos+i),
- *(track_left+i));
- #ifdef DEBUG
- (void)printf("meta-event %02x ",
- type);
- #endif
- GETVARLEN(length,*(track_pos+i),
- *(track_left+i));
- #ifdef DEBUG
- (void)printf("length %ld\n",
- length);
- #endif
- switch(type)
- { /* u-sec tempo set (not sure if this is
- correct, documents are not clear enough
- about this) */
- case 0x51:
- { LONG t;
- BYTE c;
- GET8BITS(c,*(track_pos+i),
- *(track_left+i));
- t=(LONG)c<<16;
- GET8BITS(c,*(track_pos+i),
- *(track_left+i));
- t += (LONG)c<<8;
- GET8BITS(c,*(track_pos+i),
- *(track_left+i));
- t += (LONG)c;
- #ifdef DEBUG
- (void)printf("usec %ld ",t);
- #endif
- t=(t*(LONG)CLK_TCK)/1000000L;
- time_per_beat = (double)t;
- #ifdef DEBUG
- (void)printf("our %f\n",time_per_beat);
- #endif
- break;
- };
-
- /* end of track - can't be bothered to check
- left == 0 too */
- case 0x2f:
- *(track_finished+i)=TRUE;
- finished_tracks++;
- break;
-
- /* ignore rest for now */
- default:
- CHECKLEFT(*(track_left+i),(int)length);
- *(track_pos+i) += length;
- *(track_left+i) -= length;
- break;
- };
- break;
- }
-
- /* sysex events */
- case 0xf0:
- case 0xf7:
- { LONG length;
- GETVARLEN(length,*(track_pos+i),
- *(track_left+i));
- CHECKLEFT(*(track_left+i),
- (int)length);
- /* ignoring these too */
- *(track_pos+i) += length;
- *(track_left+i) -= length;
- break;
- };
-
- /* midi events */
- default:
- { switch(event & 0xf0)
- { /* 3 byte events */
- case 0x90:
- case 0x80:
- case 0xa0:
- case 0xb0:
- case 0xe0:
- { BYTE c;
- SEND(event);
- GET8BITS(c,*(track_pos+i),
- *(track_left+i));
- SEND(c);
- GET8BITS(c,*(track_pos+i),
- *(track_left+i));
- SEND(c);
- running_status = event;
- break;
- };
- /* 2 byte events */
- case 0xc0:
- case 0xd0:
- { BYTE c;
- SEND(event);
- GET8BITS(c,*(track_pos+i),
- *(track_left+i));
- SEND(c);
- running_status = event;
- break;
- };
- default:
- { /* running status - hope a golbal one
- is OK */
- if((event&0x80)==0)
- { SEND(event);
- #ifdef DEBUG
- (void)printf(
- "running stat (%02x) \
- %02x\n",running_status,event);
- #endif
- switch(running_status & 0xf0)
- { /* 3 byte events */
- case 0x90:
- case 0x80:
- case 0xa0:
- case 0xb0:
- case 0xe0:
- { BYTE c;
- GET8BITS(c,*(track_pos+i),
- *(track_left+i));
- SEND(c);
- };
- };
- };
-
- /* ignoring other 0xf? events -
- naughty aren't I */
- };
- };
- };
- };
-
- /* get delta time - but only if we're still
- going on this track */
- if(!*(track_finished+i))
- { GETVARLEN(delta,*(track_pos+i),
- *(track_left+i));
- *(track_delta+i) += (double)delta;
- };
- };
- };
- };
- return(0);
- };
-
- /* general error messages - I got fed up typing these over and over again */
- static void truncated()
- { (void)fprintf(stderr,"%s: %s truncated\n",app_name,gFile);
- };
-
- /* THAT'S ALL FOLKS! */
-